//===--- ToolChains.cpp - Job invocations (general and per-platform) ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/driver/internal/ToolChains.h"

#include "polarphp/basic/Dwarf.h"
#include "polarphp/basic/LLVM.h"
#include "polarphp/basic/Platform.h"
#include "polarphp/basic/Range.h"
#include "polarphp/basic/StlExtras.h"
#include "polarphp/basic/TaskQueue.h"
#include "polarphp/global/Config.h"
#include "polarphp/driver/Compilation.h"
#include "polarphp/driver/Driver.h"
#include "polarphp/driver/Job.h"
#include "polarphp/option/Options.h"
#include "clang/Basic/Version.h"
#include "clang/Driver/Util.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"

using namespace polar;
using namespace polar::driver;
using namespace llvm::opt;

bool ToolChain::JobContext::shouldUseInputFileList() const {
   return getTopLevelInputFiles().size() > C.getFilelistThreshold();
}

bool ToolChain::JobContext::shouldUsePrimaryInputFileListInFrontendInvocation()
const {
   return InputActions.size() > C.getFilelistThreshold();
}

bool ToolChain::JobContext::shouldUseMainOutputFileListInFrontendInvocation()
const {
   return Output.getPrimaryOutputFilenames().size() > C.getFilelistThreshold();
}

bool ToolChain::JobContext::
shouldUseSupplementaryOutputFileMapInFrontendInvocation() const {
   static const unsigned UpperBoundOnSupplementaryOutputFileTypes =
      filetypes::TY_INVALID;
   return InputActions.size() * UpperBoundOnSupplementaryOutputFileTypes >
          C.getFilelistThreshold();
}

bool ToolChain::JobContext::shouldFilterFrontendInputsByType() const {
   // FIXME: SingleCompile has not filtered its inputs in the past and now people
   // rely upon that. But we would like the compilation modes to be consistent.
   return OI.CompilerMode != OutputInfo::Mode::SingleCompile;
}

void ToolChain::addInputsOfType(ArgStringList &Arguments,
                                ArrayRef<const Action *> Inputs,
                                filetypes::FileTypeId InputType,
                                const char *PrefixArgument) const {
   for (auto &Input : Inputs) {
      if (Input->getType() != InputType)
         continue;
      if (PrefixArgument)
         Arguments.push_back(PrefixArgument);
      Arguments.push_back(cast<InputAction>(Input)->getInputArg().getValue());
   }
}

void ToolChain::addInputsOfType(ArgStringList &Arguments,
                                ArrayRef<const Job *> Jobs,
                                const llvm::opt::ArgList &Args,
                                filetypes::FileTypeId InputType,
                                const char *PrefixArgument) const {
   for (const Job *Cmd : Jobs) {
      auto output = Cmd->getOutput().getAnyOutputForType(InputType);
      if (!output.empty()) {
         if (PrefixArgument)
            Arguments.push_back(PrefixArgument);
         Arguments.push_back(Args.MakeArgString(output));
      }
   }
}

void ToolChain::addPrimaryInputsOfType(ArgStringList &Arguments,
                                       ArrayRef<const Job *> Jobs,
                                       const llvm::opt::ArgList &Args,
                                       filetypes::FileTypeId InputType,
                                       const char *PrefixArgument) const {
   for (const Job *Cmd : Jobs) {
      auto &outputInfo = Cmd->getOutput();
      if (outputInfo.getPrimaryOutputType() == InputType) {
         for (auto Output : outputInfo.getPrimaryOutputFilenames()) {
            if (PrefixArgument)
               Arguments.push_back(PrefixArgument);
            Arguments.push_back(Args.MakeArgString(Output));
         }
      }
   }
}

static bool addOutputsOfType(ArgStringList &Arguments,
                             CommandOutput const &Output,
                             const llvm::opt::ArgList &Args,
                             filetypes::FileTypeId OutputType,
                             const char *PrefixArgument = nullptr) {
   bool Added = false;
   for (auto Output : Output.getAdditionalOutputsForType(OutputType)) {
      assert(!Output.empty());
      if (PrefixArgument)
         Arguments.push_back(PrefixArgument);
      Arguments.push_back(Args.MakeArgString(Output));
      Added = true;
   }
   return Added;
}

/// Handle arguments common to all invocations of the frontend (compilation,
/// module-merging, LLDB's REPL, etc).
static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI,
                                  const CommandOutput &output,
                                  const ArgList &inputArgs,
                                  ArgStringList &arguments) {
   const llvm::Triple &Triple = TC.getTriple();

   // Only pass -target to the REPL or immediate modes if it was explicitly
   // specified on the command line.
   switch (OI.CompilerMode) {
      case OutputInfo::Mode::REPL:
      case OutputInfo::Mode::Immediate:
         if (!inputArgs.hasArg(options::OPT_target))
            break;
         LLVM_FALLTHROUGH;
      case OutputInfo::Mode::StandardCompile:
      case OutputInfo::Mode::SingleCompile:
      case OutputInfo::Mode::BatchModeCompile:
         arguments.push_back("-target");
         arguments.push_back(inputArgs.MakeArgString(Triple.str()));
         break;
   }

   // Enable address top-byte ignored in the ARM64 backend.
   if (Triple.getArch() == llvm::Triple::aarch64) {
      arguments.push_back("-Xllvm");
      arguments.push_back("-aarch64-use-tbi");
   }

   // Enable or disable ObjC interop appropriately for the platform
   if (Triple.isOSDarwin()) {
      arguments.push_back("-enable-objc-interop");
   } else {
      arguments.push_back("-disable-objc-interop");
   }

   // Handle the CPU and its preferences.
   inputArgs.AddLastArg(arguments, options::OPT_target_cpu);

   if (!OI.SDKPath.empty()) {
      arguments.push_back("-sdk");
      arguments.push_back(inputArgs.MakeArgString(OI.SDKPath));
   }

   if (llvm::sys::Process::StandardErrHasColors()) {
      arguments.push_back("-color-diagnostics");
   }

   inputArgs.AddAllArgs(arguments, options::OPT_I);
   inputArgs.AddAllArgs(arguments, options::OPT_F, options::OPT_Fsystem);

   inputArgs.AddLastArg(arguments, options::OPT_AssertConfig);
   inputArgs.AddLastArg(arguments, options::OPT_autolink_force_load);
   inputArgs.AddLastArg(arguments,
                        options::OPT_color_diagnostics,
                        options::OPT_no_color_diagnostics);
   inputArgs.AddLastArg(arguments, options::OPT_fixit_all);
//   inputArgs.AddLastArg(arguments,
//                        options::OPT_warn_swift3_objc_inference_minimal,
//                        options::OPT_warn_swift3_objc_inference_complete);
   inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
   inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
   inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);
   inputArgs.AddLastArg(arguments, options::OPT_enable_library_evolution);
   inputArgs.AddLastArg(arguments, options::OPT_enable_testing);
   inputArgs.AddLastArg(arguments, options::OPT_enable_private_imports);
   inputArgs.AddLastArg(arguments, options::OPT_enable_cxx_interop);
   inputArgs.AddLastArg(arguments, options::OPT_g_Group);
   inputArgs.AddLastArg(arguments, options::OPT_debug_info_format);
   inputArgs.AddLastArg(arguments, options::OPT_import_underlying_module);
   inputArgs.AddLastArg(arguments, options::OPT_module_cache_path);
   inputArgs.AddLastArg(arguments, options::OPT_module_link_name);
   inputArgs.AddLastArg(arguments, options::OPT_nostdimport);
   inputArgs.AddLastArg(arguments, options::OPT_parse_stdlib);
   inputArgs.AddLastArg(arguments, options::OPT_resource_dir);
   inputArgs.AddLastArg(arguments, options::OPT_solver_memory_threshold);
   inputArgs.AddLastArg(arguments, options::OPT_value_recursion_threshold);
//   inputArgs.AddLastArg(arguments, options::OPT_warn_swift3_objc_inference);
   inputArgs.AddLastArg(arguments, options::OPT_Rpass_EQ);
   inputArgs.AddLastArg(arguments, options::OPT_Rpass_missed_EQ);
   inputArgs.AddLastArg(arguments, options::OPT_suppress_warnings);
   inputArgs.AddLastArg(arguments, options::OPT_profile_generate);
   inputArgs.AddLastArg(arguments, options::OPT_profile_use);
   inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);
   inputArgs.AddLastArg(arguments, options::OPT_warnings_as_errors);
   inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ);
   inputArgs.AddLastArg(arguments, options::OPT_sanitize_recover_EQ);
   inputArgs.AddLastArg(arguments, options::OPT_sanitize_coverage_EQ);
   inputArgs.AddLastArg(arguments, options::OPT_static);
   inputArgs.AddLastArg(arguments, options::OPT_polarphp_version);
   inputArgs.AddLastArg(arguments, options::OPT_enforce_exclusivity_EQ);
   inputArgs.AddLastArg(arguments, options::OPT_stats_output_dir);
   inputArgs.AddLastArg(arguments, options::OPT_trace_stats_events);
   inputArgs.AddLastArg(arguments, options::OPT_profile_stats_events);
   inputArgs.AddLastArg(arguments, options::OPT_profile_stats_entities);
   inputArgs.AddLastArg(arguments,
                        options::OPT_solver_shrink_unsolved_threshold);
   inputArgs.AddLastArg(arguments, options::OPT_O_Group);
   inputArgs.AddLastArg(arguments, options::OPT_RemoveRuntimeAsserts);
   inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded);
   inputArgs.AddLastArg(arguments,
                        options::OPT_enable_fine_grained_dependencies);
   inputArgs.AddLastArg(arguments,
                        options::OPT_fine_grained_dependency_include_intrafile);
   inputArgs.AddLastArg(arguments, options::OPT_package_description_version);
   inputArgs.AddLastArg(arguments, options::OPT_serialize_diagnostics_path);
   inputArgs.AddLastArg(arguments, options::OPT_debug_diagnostic_names);
   inputArgs.AddLastArg(arguments, options::OPT_enable_astscope_lookup);
   inputArgs.AddLastArg(arguments, options::OPT_disable_astscope_lookup);
   inputArgs.AddLastArg(arguments, options::OPT_disable_parser_lookup);

   // Pass on any build config options
   inputArgs.AddAllArgs(arguments, options::OPT_D);

   // Pass on file paths that should be remapped in debug info.
   inputArgs.AddAllArgs(arguments, options::OPT_debug_prefix_map);

   // Pass through the values passed to -Xfrontend.
   inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend);

   if (auto *A = inputArgs.getLastArg(options::OPT_working_directory)) {
      // Add -Xcc -working-directory before any other -Xcc options to ensure it is
      // overridden by an explicit -Xcc -working-directory, although having a
      // different working directory is probably incorrect.
      SmallString<128> workingDirectory(A->getValue());
      llvm::sys::fs::make_absolute(workingDirectory);
      arguments.push_back("-Xcc");
      arguments.push_back("-working-directory");
      arguments.push_back("-Xcc");
      arguments.push_back(inputArgs.MakeArgString(workingDirectory));
   }

   // -g implies -enable-anonymous-context-mangled-names, because the extra
   // metadata aids debugging.
   if (inputArgs.hasArg(options::OPT_g)) {
      // But don't add the option in optimized builds: it would prevent dead code
      // stripping of unused metadata.
      auto OptArg = inputArgs.getLastArgNoClaim(options::OPT_O_Group);
      if (!OptArg || OptArg->getOption().matches(options::OPT_Onone))
         arguments.push_back("-enable-anonymous-context-mangled-names");
   }

   // Pass through any subsystem flags.
   inputArgs.AddAllArgs(arguments, options::OPT_Xllvm);
   inputArgs.AddAllArgs(arguments, options::OPT_Xcc);
}

static void addRuntimeLibraryFlags(const OutputInfo &OI,
                                   ArgStringList &Arguments) {
   if (!OI.RuntimeVariant)
      return;

   const OutputInfo::MSVCRuntime RT = OI.RuntimeVariant.getValue();

   Arguments.push_back("-autolink-library");
   Arguments.push_back("oldnames");

   Arguments.push_back("-autolink-library");
   switch (RT) {
      case OutputInfo::MSVCRuntime::MultiThreaded:
         Arguments.push_back("libcmt");
         break;

      case OutputInfo::MSVCRuntime::MultiThreadedDebug:
         Arguments.push_back("libcmtd");
         break;

      case OutputInfo::MSVCRuntime::MultiThreadedDLL:
         Arguments.push_back("msvcrt");
         break;

      case OutputInfo::MSVCRuntime::MultiThreadedDebugDLL:
         Arguments.push_back("msvcrtd");
         break;
   }

   // NOTE(compnerd) we do not support /ML and /MLd
   Arguments.push_back("-Xcc");
   Arguments.push_back("-D_MT");

   if (RT == OutputInfo::MSVCRuntime::MultiThreadedDLL ||
       RT == OutputInfo::MSVCRuntime::MultiThreadedDebugDLL) {
      Arguments.push_back("-Xcc");
      Arguments.push_back("-D_DLL");
   }
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const CompileJobAction &job,
                               const JobContext &context) const {
   InvocationInfo II{POLARPHP_EXECUTABLE_NAME};
   ArgStringList &Arguments = II.Arguments;
   II.allowsResponseFiles = true;

   for (auto &s : getDriver().getPHPProgramArgs())
      Arguments.push_back(s.c_str());
   Arguments.push_back("-frontend");

   {
      // Determine the frontend mode option.
      const char *FrontendModeOption = context.computeFrontendModeForCompile();
      assert(FrontendModeOption != nullptr &&
             "No frontend mode option specified!");
      Arguments.push_back(FrontendModeOption);
   }

   context.addFrontendInputAndOutputArguments(Arguments, II.FilelistInfos);

   // Forward migrator flags.
   if (auto DataPath =
      context.Args.getLastArg(options::OPT_api_diff_data_file)) {
      Arguments.push_back("-api-diff-data-file");
      Arguments.push_back(DataPath->getValue());
   }
   if (auto DataDir = context.Args.getLastArg(options::OPT_api_diff_data_dir)) {
      Arguments.push_back("-api-diff-data-dir");
      Arguments.push_back(DataDir->getValue());
   }
   if (context.Args.hasArg(options::OPT_dump_usr)) {
      Arguments.push_back("-dump-usr");
   }

   if (context.Args.hasArg(options::OPT_parse_stdlib))
      Arguments.push_back("-disable-objc-attr-requires-foundation-module");

   addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
                         Arguments);
   addRuntimeLibraryFlags(context.OI, Arguments);

   // Pass along an -import-objc-header arg, replacing the argument with the name
   // of any input PCH to the current action if one is present.
   if (context.Args.hasArgNoClaim(options::OPT_import_objc_header)) {
      bool ForwardAsIs = true;
      bool bridgingPCHIsEnabled =
         context.Args.hasFlag(options::OPT_enable_bridging_pch,
                              options::OPT_disable_bridging_pch, true);
      bool usePersistentPCH = bridgingPCHIsEnabled &&
                              context.Args.hasArg(options::OPT_pch_output_dir);
      if (!usePersistentPCH) {
         for (auto *IJ : context.Inputs) {
            if (!IJ->getOutput().getAnyOutputForType(filetypes::TY_PCH).empty()) {
               Arguments.push_back("-import-objc-header");
               addInputsOfType(Arguments, context.Inputs, context.Args,
                               filetypes::TY_PCH);
               ForwardAsIs = false;
               break;
            }
         }
      }
      if (ForwardAsIs) {
         context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
      }
      if (usePersistentPCH) {
         context.Args.AddLastArg(Arguments, options::OPT_pch_output_dir);
         if (context.OI.CompilerMode == OutputInfo::Mode::StandardCompile) {
            // In the 'multiple invocations for each file' mode we don't need to
            // validate the PCH every time, it has been validated with the initial
            // -emit-pch invocation.
            Arguments.push_back("-pch-disable-validation");
         }
      }
   }

   if (context.Args.hasArg(options::OPT_parse_as_library) ||
       context.Args.hasArg(options::OPT_emit_library))
      Arguments.push_back("-parse-as-library");

   context.Args.AddLastArg(Arguments, options::OPT_parse_pil);

   Arguments.push_back("-module-name");
   Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

   if (context.Args.hasArg(options::OPT_CrossModuleOptimization)) {
      Arguments.push_back("-cross-module-optimization");
   }

   addOutputsOfType(Arguments, context.Output, context.Args,
                    filetypes::TY_OptRecord, "-save-optimization-record-path");

   if (context.Args.hasArg(options::OPT_migrate_keep_objc_visibility)) {
      Arguments.push_back("-migrate-keep-objc-visibility");
   }

   addOutputsOfType(Arguments, context.Output, context.Args,
                    filetypes::TY_Remapping, "-emit-remap-file-path");

   if (context.OI.numThreads > 0) {
      Arguments.push_back("-num-threads");
      Arguments.push_back(
         context.Args.MakeArgString(Twine(context.OI.numThreads)));
   }

   // Add the output file argument if necessary.
   if (context.Output.getPrimaryOutputType() != filetypes::TY_Nothing) {
      if (context.shouldUseMainOutputFileListInFrontendInvocation()) {
         Arguments.push_back("-output-filelist");
         Arguments.push_back(context.getTemporaryFilePath("outputs", ""));
         II.FilelistInfos.push_back({Arguments.back(),
                                     context.Output.getPrimaryOutputType(),
                                     FilelistInfo::WhichFiles::Output});
      } else {
         for (auto FileName : context.Output.getPrimaryOutputFilenames()) {
            Arguments.push_back("-o");
            Arguments.push_back(context.Args.MakeArgString(FileName));
         }
      }
   }

   if (context.Args.hasArg(options::OPT_embed_bitcode_marker))
      Arguments.push_back("-embed-bitcode-marker");

   // For `-index-file` mode add `-disable-typo-correction`, since the errors
   // will be ignored and it can be expensive to do typo-correction.
   if (job.getType() == filetypes::TY_IndexData) {
      Arguments.push_back("-disable-typo-correction");
   }

   if (context.Args.hasArg(options::OPT_index_store_path)) {
      context.Args.AddLastArg(Arguments, options::OPT_index_store_path);
      if (!context.Args.hasArg(options::OPT_index_ignore_system_modules))
         Arguments.push_back("-index-system-modules");
   }

   if (context.Args.hasArg(options::OPT_debug_info_store_invocation) ||
       shouldStoreInvocationInDebugInfo()) {
      Arguments.push_back("-debug-info-store-invocation");
   }

   if (context.Args.hasArg(
      options::OPT_disable_autolinking_runtime_compatibility)) {
      Arguments.push_back("-disable-autolinking-runtime-compatibility");
   }

   if (auto arg = context.Args.getLastArg(
      options::OPT_runtime_compatibility_version)) {
      Arguments.push_back("-runtime-compatibility-version");
      Arguments.push_back(arg->getValue());
   }

   context.Args.AddLastArg(
      Arguments,
      options::
      OPT_disable_autolinking_runtime_compatibility_dynamic_replacements);

   return II;
}

const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
   switch (OI.CompilerMode) {
      case OutputInfo::Mode::StandardCompile:
      case OutputInfo::Mode::SingleCompile:
      case OutputInfo::Mode::BatchModeCompile:
         break;
      case OutputInfo::Mode::Immediate:
      case OutputInfo::Mode::REPL:
         llvm_unreachable("REPL and immediate modes handled elsewhere");
   }
   switch (Output.getPrimaryOutputType()) {
      case filetypes::TY_Object:
         return "-c";
      case filetypes::TY_PCH:
         return "-emit-pch";
      case filetypes::TY_ASTDump:
         return "-dump-ast";
      case filetypes::TY_RawPIL:
         return "-emit-silgen";
      case filetypes::TY_PIL:
         return "-emit-sil";
      case filetypes::TY_RawPIB:
         return "-emit-sibgen";
      case filetypes::TY_PIB:
         return "-emit-sib";
      case filetypes::TY_LLVM_IR:
         return "-emit-ir";
      case filetypes::TY_LLVM_BC:
         return "-emit-bc";
      case filetypes::TY_ClangModuleFile:
         return "-emit-pcm";
      case filetypes::TY_Assembly:
         return "-S";
      case filetypes::TY_PHPModuleFile:
         // Since this is our primary output, we need to specify the option here.
         return "-emit-module";
      case filetypes::TY_ImportedModules:
         return "-emit-imported-modules";
      case filetypes::TY_IndexData:
         return "-typecheck";
      case filetypes::TY_Remapping:
         return "-update-code";
      case filetypes::TY_Nothing:
         // We were told to output nothing, so get the last mode option and use that.
         if (const Arg *A = Args.getLastArg(options::OPT_modes_Group))
            return A->getSpelling().data();
         else
            llvm_unreachable("We were told to perform a standard compile, "
                             "but no mode option was passed to the driver.");
      case filetypes::TY_PHP:
      case filetypes::TY_dSYM:
      case filetypes::TY_AutolinkFile:
      case filetypes::TY_Dependencies:
      case filetypes::TY_PHPModuleDocFile:
      case filetypes::TY_SerializedDiagnostics:
//      case filetypes::TY_ObjCHeader:
      case filetypes::TY_Image:
      case filetypes::TY_PHPDeps:
      case filetypes::TY_PHPRanges:
      case filetypes::TY_CompiledSource:
      case filetypes::TY_ModuleTrace:
      case filetypes::TY_TBD:
      case filetypes::TY_OptRecord:
      case filetypes::TY_PHPModuleInterfaceFile:
      case filetypes::TY_PHPSourceInfoFile:
         llvm_unreachable("Output type can never be primary output.");
      case filetypes::TY_INVALID:
         llvm_unreachable("Invalid type ID");
   }
   llvm_unreachable("unhandled output type");
}

void ToolChain::JobContext::addFrontendInputAndOutputArguments(
   ArgStringList &Arguments, std::vector<FilelistInfo> &FilelistInfos) const {

   switch (OI.CompilerMode) {
      case OutputInfo::Mode::StandardCompile:
         assert(InputActions.size() == 1 &&
                "Standard-compile mode takes exactly one input (the primary file)");
         break;
      case OutputInfo::Mode::BatchModeCompile:
      case OutputInfo::Mode::SingleCompile:
         break;
      case OutputInfo::Mode::Immediate:
      case OutputInfo::Mode::REPL:
         llvm_unreachable("REPL and immediate modes handled elsewhere");
   }

   const bool UseFileList = shouldUseInputFileList();
   const bool MayHavePrimaryInputs = OI.mightHaveExplicitPrimaryInputs(Output);
   const bool UsePrimaryFileList =
      MayHavePrimaryInputs &&
      shouldUsePrimaryInputFileListInFrontendInvocation();
   const bool FilterInputsByType = shouldFilterFrontendInputsByType();
   const bool UseSupplementaryOutputFileList =
      shouldUseSupplementaryOutputFileMapInFrontendInvocation();

   assert((C.getFilelistThreshold() != Compilation::NEVER_USE_FILELIST ||
           !UseFileList && !UsePrimaryFileList &&
           !UseSupplementaryOutputFileList) &&
          "No filelists are used if FilelistThreshold=NEVER_USE_FILELIST");

   if (UseFileList) {
      Arguments.push_back("-filelist");
      Arguments.push_back(getAllSourcesPath());
   }
   if (UsePrimaryFileList) {
      Arguments.push_back("-primary-filelist");
      Arguments.push_back(getTemporaryFilePath("primaryInputs", ""));
      FilelistInfos.push_back({Arguments.back(), filetypes::TY_PHP,
                               FilelistInfo::WhichFiles::SourceInputActions});
   }
   if (!UseFileList || !UsePrimaryFileList) {
      addFrontendCommandLineInputArguments(MayHavePrimaryInputs, UseFileList,
                                           UsePrimaryFileList, FilterInputsByType,
                                           Arguments);
   }

   if (UseSupplementaryOutputFileList) {
      Arguments.push_back("-supplementary-output-file-map");
      Arguments.push_back(getTemporaryFilePath("supplementaryOutputs", ""));
      FilelistInfos.push_back({Arguments.back(), filetypes::TY_INVALID,
                               FilelistInfo::WhichFiles::SupplementaryOutput});
   } else {
      addFrontendSupplementaryOutputArguments(Arguments);
   }
}

void ToolChain::JobContext::addFrontendCommandLineInputArguments(
   const bool mayHavePrimaryInputs, const bool useFileList,
   const bool usePrimaryFileList, const bool filterByType,
   ArgStringList &arguments) const {
   llvm::DenseSet<StringRef> primaries;

   if (mayHavePrimaryInputs) {
      for (const Action *A : InputActions) {
         const auto *IA = cast<InputAction>(A);
         const llvm::opt::Arg &InArg = IA->getInputArg();
         primaries.insert(InArg.getValue());
      }
   }
   // -index-file compilations are weird. They are processed as SingleCompiles
   // (WMO), but must indicate that there is one primary file, designated by
   // -index-file-path.
   if (Arg *A = Args.getLastArg(options::OPT_index_file_path)) {
      assert(primaries.empty() &&
             "index file jobs should be treated as single (WMO) compiles");
      primaries.insert(A->getValue());
   }
   for (auto inputPair : getTopLevelInputFiles()) {
      if (filterByType && !filetypes::is_part_of_php_compilation(inputPair.first))
         continue;
      const char *inputName = inputPair.second->getValue();
      const bool isPrimary = primaries.count(inputName);
      if (isPrimary && !usePrimaryFileList) {
         arguments.push_back("-primary-file");
         arguments.push_back(inputName);
      }
      if ((!isPrimary || usePrimaryFileList) && !useFileList)
         arguments.push_back(inputName);
   }
}

void ToolChain::JobContext::addFrontendSupplementaryOutputArguments(
   ArgStringList &arguments) const {
   // FIXME: Get these and other argument strings from the same place for both
   // driver and frontend.
   addOutputsOfType(arguments, Output, Args, filetypes::FileTypeId::TY_PHPModuleFile,
                    "-emit-module-path");

   addOutputsOfType(arguments, Output, Args, filetypes::TY_PHPModuleDocFile,
                    "-emit-module-doc-path");

   addOutputsOfType(arguments, Output, Args, filetypes::TY_PHPSourceInfoFile,
                    "-emit-module-source-info-path");

   addOutputsOfType(arguments, Output, Args,
                    filetypes::FileTypeId::TY_PHPModuleInterfaceFile,
                    "-emit-module-interface-path");

   addOutputsOfType(arguments, Output, Args,
                    filetypes::TY_SerializedDiagnostics,
                    "-serialize-diagnostics-path");

//   if (addOutputsOfType(arguments, Output, Args, filetypes::FileTypeId::TY_ObjCHeader,
//                        "-emit-objc-header-path")) {
//      assert(OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
//             "The Swift tool should only emit an Obj-C header in single compile"
//             "mode!");
//   }

   addOutputsOfType(arguments, Output, Args, filetypes::TY_Dependencies,
                    "-emit-dependencies-path");
   addOutputsOfType(arguments, Output, Args, filetypes::TY_PHPDeps,
                    "-emit-reference-dependencies-path");
   addOutputsOfType(arguments, Output, Args, filetypes::TY_PHPRanges,
                    "-emit-swift-ranges-path");
   addOutputsOfType(arguments, Output, Args, filetypes::TY_CompiledSource,
                    "-emit-compiled-source-path");
   addOutputsOfType(arguments, Output, Args, filetypes::TY_ModuleTrace,
                    "-emit-loaded-module-trace-path");
   addOutputsOfType(arguments, Output, Args, filetypes::TY_TBD,
                    "-emit-tbd-path");
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const InterpretJobAction &job,
                               const JobContext &context) const {
   assert(context.OI.CompilerMode == OutputInfo::Mode::Immediate);

   InvocationInfo II{POLARPHP_EXECUTABLE_NAME};
   ArgStringList &Arguments = II.Arguments;
   II.allowsResponseFiles = true;

   for (auto &s : getDriver().getPHPProgramArgs())
      Arguments.push_back(s.c_str());
   Arguments.push_back("-frontend");
   Arguments.push_back("-interpret");

   assert(context.Inputs.empty() &&
          "The Swift frontend does not expect to be fed any input Jobs!");

   for (const Action *A : context.InputActions) {
      cast<InputAction>(A)->getInputArg().render(context.Args, Arguments);
   }

   if (context.Args.hasArg(options::OPT_parse_stdlib))
      Arguments.push_back("-disable-objc-attr-requires-foundation-module");

   addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
                         Arguments);
   addRuntimeLibraryFlags(context.OI, Arguments);

   context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);

   context.Args.AddLastArg(Arguments, options::OPT_parse_pil);

   Arguments.push_back("-module-name");
   Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

   context.Args.AddAllArgs(Arguments, options::OPT_l, options::OPT_framework);

   // The immediate arguments must be last.
   context.Args.AddLastArg(Arguments, options::OPT__DASH_DASH);

   return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const BackendJobAction &job,
                               const JobContext &context) const {
   assert(context.Args.hasArg(options::OPT_embed_bitcode));
   ArgStringList Arguments;

   for (auto &s : getDriver().getPHPProgramArgs())
      Arguments.push_back(s.c_str());
   Arguments.push_back("-frontend");

   // Determine the frontend mode option.
   const char *FrontendModeOption = nullptr;
   switch (context.OI.CompilerMode) {
      case OutputInfo::Mode::StandardCompile:
      case OutputInfo::Mode::SingleCompile: {
         switch (context.Output.getPrimaryOutputType()) {
            case filetypes::TY_Object:
               FrontendModeOption = "-c";
               break;
            case filetypes::TY_LLVM_IR:
               FrontendModeOption = "-emit-ir";
               break;
            case filetypes::TY_LLVM_BC:
               FrontendModeOption = "-emit-bc";
               break;
            case filetypes::TY_Assembly:
               FrontendModeOption = "-S";
               break;
            case filetypes::TY_Nothing:
               // We were told to output nothing, so get the last mode option and use
               // that.
               if (const Arg *A = context.Args.getLastArg(options::OPT_modes_Group))
                  FrontendModeOption = A->getSpelling().data();
               else
                  llvm_unreachable("We were told to perform a standard compile, "
                                   "but no mode option was passed to the driver.");
               break;

            case filetypes::TY_ImportedModules:
            case filetypes::TY_TBD:
            case filetypes::TY_PHPModuleFile:
            case filetypes::TY_ASTDump:
            case filetypes::TY_RawPIL:
            case filetypes::TY_RawPIB:
            case filetypes::TY_PIL:
            case filetypes::TY_PIB:
            case filetypes::TY_PCH:
            case filetypes::TY_ClangModuleFile:
            case filetypes::TY_IndexData:
               llvm_unreachable("Cannot be output from backend job");
            case filetypes::TY_PHP:
            case filetypes::TY_dSYM:
            case filetypes::TY_AutolinkFile:
            case filetypes::TY_Dependencies:
            case filetypes::TY_PHPModuleDocFile:
            case filetypes::TY_SerializedDiagnostics:
//            case filetypes::TY_ObjCHeader:
            case filetypes::TY_Image:
            case filetypes::TY_PHPDeps:
            case filetypes::TY_PHPRanges:
            case filetypes::TY_CompiledSource:
            case filetypes::TY_Remapping:
            case filetypes::TY_ModuleTrace:
            case filetypes::TY_OptRecord:
            case filetypes::TY_PHPModuleInterfaceFile:
            case filetypes::TY_PHPSourceInfoFile:
               llvm_unreachable("Output type can never be primary output.");
            case filetypes::TY_INVALID:
               llvm_unreachable("Invalid type ID");
         }
         break;
      }
      case OutputInfo::Mode::BatchModeCompile:
      case OutputInfo::Mode::Immediate:
      case OutputInfo::Mode::REPL:
         llvm_unreachable("invalid mode for backend job");
   }

   assert(FrontendModeOption != nullptr && "No frontend mode option specified!");

   Arguments.push_back(FrontendModeOption);

   // Add input arguments.
   switch (context.OI.CompilerMode) {
      case OutputInfo::Mode::StandardCompile: {
         assert(context.Inputs.size() == 1 && "The backend expects one input!");
         Arguments.push_back("-primary-file");
         const Job *Cmd = context.Inputs.front();
         Arguments.push_back(context.Args.MakeArgString(
            Cmd->getOutput().getPrimaryOutputFilename()));
         break;
      }
      case OutputInfo::Mode::SingleCompile: {
         assert(context.Inputs.size() == 1 && "The backend expects one input!");
         Arguments.push_back("-primary-file");
         const Job *Cmd = context.Inputs.front();

         // In multi-threaded compilation, the backend job must select the correct
         // output file of the compilation job.
         auto OutNames = Cmd->getOutput().getPrimaryOutputFilenames();
         Arguments.push_back(
            context.Args.MakeArgString(OutNames[job.getInputIndex()]));
         break;
      }
      case OutputInfo::Mode::BatchModeCompile:
      case OutputInfo::Mode::Immediate:
      case OutputInfo::Mode::REPL:
         llvm_unreachable("invalid mode for backend job");
   }

   // Add flags implied by -embed-bitcode.
   Arguments.push_back("-embed-bitcode");

   // -embed-bitcode only supports a restricted set of flags.
   Arguments.push_back("-target");
   Arguments.push_back(context.Args.MakeArgString(getTriple().str()));

   // Enable address top-byte ignored in the ARM64 backend.
   if (getTriple().getArch() == llvm::Triple::aarch64) {
      Arguments.push_back("-Xllvm");
      Arguments.push_back("-aarch64-use-tbi");
   }

   // Handle the CPU and its preferences.
   context.Args.AddLastArg(Arguments, options::OPT_target_cpu);

   // Enable optimizations, but disable all LLVM-IR-level transformations.
   context.Args.AddLastArg(Arguments, options::OPT_O_Group);
   Arguments.push_back("-disable-llvm-optzns");

   context.Args.AddLastArg(Arguments, options::OPT_parse_stdlib);

   Arguments.push_back("-module-name");
   Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

   // Add the output file argument if necessary.
   if (context.Output.getPrimaryOutputType() != filetypes::TY_Nothing) {
      for (auto FileName : context.Output.getPrimaryOutputFilenames()) {
         Arguments.push_back("-o");
         Arguments.push_back(context.Args.MakeArgString(FileName));
      }
   }

   return {POLARPHP_EXECUTABLE_NAME, Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const MergeModuleJobAction &job,
                               const JobContext &context) const {
   InvocationInfo II{POLARPHP_EXECUTABLE_NAME};
   ArgStringList &Arguments = II.Arguments;
   II.allowsResponseFiles = true;

   for (auto &s : getDriver().getPHPProgramArgs())
      Arguments.push_back(s.c_str());
   Arguments.push_back("-frontend");

   Arguments.push_back("-merge-modules");
   Arguments.push_back("-emit-module");

   if (context.shouldUseInputFileList()) {
      Arguments.push_back("-filelist");
      Arguments.push_back(context.getTemporaryFilePath("inputs", ""));
      II.FilelistInfos.push_back({Arguments.back(),
                                  filetypes::TY_PHPModuleFile,
                                  FilelistInfo::WhichFiles::InputJobs});

      addInputsOfType(Arguments, context.InputActions,
                      filetypes::TY_PHPModuleFile);
   } else {
      size_t origLen = Arguments.size();
      (void)origLen;
      addInputsOfType(Arguments, context.Inputs, context.Args,
                      filetypes::TY_PHPModuleFile);
      addInputsOfType(Arguments, context.InputActions,
                      filetypes::TY_PHPModuleFile);
      assert(Arguments.size() - origLen >=
             context.Inputs.size() + context.InputActions.size() ||
             context.OI.CompilerOutputType == filetypes::TY_Nothing);
      assert((Arguments.size() - origLen == context.Inputs.size() ||
              context.OI.CompilerOutputType == filetypes::TY_Nothing ||
              !context.InputActions.empty()) &&
             "every input to MergeModule must generate a phpmodule");
   }

   // Tell all files to parse as library, which is necessary to load them as
   // serialized ASTs.
   Arguments.push_back("-parse-as-library");

   // Merge serialized PIL from partial modules.
   Arguments.push_back("-sil-merge-partial-modules");

   // Disable PIL optimization passes; we've already optimized the code in each
   // partial mode.
   Arguments.push_back("-disable-diagnostic-passes");
   Arguments.push_back("-disable-sil-perf-optzns");

   addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
                         Arguments);
   addRuntimeLibraryFlags(context.OI, Arguments);

   addOutputsOfType(Arguments, context.Output, context.Args,
                    filetypes::TY_PHPModuleDocFile, "-emit-module-doc-path");
   addOutputsOfType(Arguments, context.Output, context.Args,
                    filetypes::TY_PHPSourceInfoFile,
                    "-emit-module-source-info-path");
   addOutputsOfType(Arguments, context.Output, context.Args,
                    filetypes::FileTypeId::TY_PHPModuleInterfaceFile,
                    "-emit-module-interface-path");
   addOutputsOfType(Arguments, context.Output, context.Args,
                    filetypes::TY_SerializedDiagnostics,
                    "-serialize-diagnostics-path");
//   addOutputsOfType(Arguments, context.Output, context.Args,
//                    filetypes::TY_ObjCHeader, "-emit-objc-header-path");
   addOutputsOfType(Arguments, context.Output, context.Args, filetypes::TY_TBD,
                    "-emit-tbd-path");

   context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);

   Arguments.push_back("-module-name");
   Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

   assert(context.Output.getPrimaryOutputType() ==
          filetypes::TY_PHPModuleFile &&
          "The MergeModule tool only produces phpmodule files!");

   Arguments.push_back("-o");
   Arguments.push_back(
      context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));

   return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const ModuleWrapJobAction &job,
                               const JobContext &context) const {
   InvocationInfo II{POLARPHP_EXECUTABLE_NAME};
   ArgStringList &Arguments = II.Arguments;
   II.allowsResponseFiles = true;

   for (auto &s : getDriver().getPHPProgramArgs())
      Arguments.push_back(s.c_str());
   Arguments.push_back("-modulewrap");

   addInputsOfType(Arguments, context.Inputs, context.Args,
                   filetypes::TY_PHPModuleFile);
   addInputsOfType(Arguments, context.InputActions,
                   filetypes::TY_PHPModuleFile);
   assert(Arguments.size() == 2 &&
          "ModuleWrap expects exactly one merged phpmodule as input");

   assert(context.Output.getPrimaryOutputType() == filetypes::TY_Object &&
          "The -modulewrap mode only produces object files");

   Arguments.push_back("-target");
   Arguments.push_back(context.Args.MakeArgString(getTriple().str()));

   Arguments.push_back("-o");
   Arguments.push_back(
      context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));

   return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const REPLJobAction &job,
                               const JobContext &context) const {
   assert(context.Inputs.empty());
   assert(context.InputActions.empty());

   bool useLLDB;

   switch (job.getRequestedMode()) {
      case REPLJobAction::Mode::Integrated:
         useLLDB = false;
         break;
      case REPLJobAction::Mode::RequireLLDB:
         useLLDB = true;
         break;
      case REPLJobAction::Mode::PreferLLDB:
         useLLDB = !findProgramRelativeToPHP("lldb").empty();
         break;
   }

   ArgStringList FrontendArgs;
   for (auto &s : getDriver().getPHPProgramArgs())
      FrontendArgs.push_back(s.c_str());

   addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
                         FrontendArgs);
   addRuntimeLibraryFlags(context.OI, FrontendArgs);

   context.Args.AddLastArg(FrontendArgs, options::OPT_import_objc_header);
   context.Args.AddAllArgs(FrontendArgs, options::OPT_l, options::OPT_framework,
                           options::OPT_L);

   if (!useLLDB) {
      FrontendArgs.insert(FrontendArgs.begin(), {"-frontend", "-repl"});
      FrontendArgs.push_back("-module-name");
      FrontendArgs.push_back(context.Args.MakeArgString(context.OI.ModuleName));
      return {POLARPHP_EXECUTABLE_NAME, FrontendArgs};
   }

   // Squash important frontend options into a single argument for LLDB.
   std::string SingleArg = "--repl=";
   {
      llvm::raw_string_ostream os(SingleArg);
      Job::printArguments(os, FrontendArgs);
   }

   ArgStringList Arguments;
   Arguments.push_back(context.Args.MakeArgString(std::move(SingleArg)));

   return {"lldb", Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const GenerateDSYMJobAction &job,
                               const JobContext &context) const {
   assert(context.Inputs.size() == 1);
   assert(context.InputActions.empty());
   assert(context.Output.getPrimaryOutputType() == filetypes::TY_dSYM);

   ArgStringList Arguments;

   auto inputPath =
      context.Inputs.front()->getOutput().getPrimaryOutputFilename();
   Arguments.push_back(context.Args.MakeArgString(inputPath));

   Arguments.push_back("-o");
   Arguments.push_back(
      context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));

   return {"dsymutil", Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const VerifyDebugInfoJobAction &job,
                               const JobContext &context) const {
   assert(context.Inputs.size() == 1);
   assert(context.InputActions.empty());

   // This mirrors the clang driver's --verify-debug-info option.
   ArgStringList Arguments;
   Arguments.push_back("--verify");
   Arguments.push_back("--debug-info");
   Arguments.push_back("--eh-frame");
   Arguments.push_back("--quiet");

   auto inputPath =
      context.Inputs.front()->getOutput().getPrimaryOutputFilename();
   Arguments.push_back(context.Args.MakeArgString(inputPath));

   return {"dwarfdump", Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const GeneratePCHJobAction &job,
                               const JobContext &context) const {
   assert(context.Inputs.empty());
   assert(context.InputActions.size() == 1);
   assert((!job.isPersistentPCH() &&
           context.Output.getPrimaryOutputType() == filetypes::TY_PCH) ||
          (job.isPersistentPCH() &&
           context.Output.getPrimaryOutputType() == filetypes::TY_Nothing));

   InvocationInfo II{POLARPHP_EXECUTABLE_NAME};
   ArgStringList &Arguments = II.Arguments;
   II.allowsResponseFiles = true;

   for (auto &s : getDriver().getPHPProgramArgs())
      Arguments.push_back(s.c_str());
   Arguments.push_back("-frontend");

   addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
                         Arguments);
   addRuntimeLibraryFlags(context.OI, Arguments);

   addOutputsOfType(Arguments, context.Output, context.Args,
                    filetypes::TY_SerializedDiagnostics,
                    "-serialize-diagnostics-path");

//   addInputsOfType(Arguments, context.InputActions, filetypes::TY_ObjCHeader);
   context.Args.AddLastArg(Arguments, options::OPT_index_store_path);

   if (job.isPersistentPCH()) {
      Arguments.push_back("-emit-pch");
      Arguments.push_back("-pch-output-dir");
      Arguments.push_back(context.Args.MakeArgString(job.getPersistentPCHDir()));
   } else {
      Arguments.push_back("-emit-pch");
      Arguments.push_back("-o");
      Arguments.push_back(
         context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
   }

   return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const AutolinkExtractJobAction &job,
                               const JobContext &context) const {
   llvm_unreachable("autolink extraction not implemented for this toolchain");
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const DynamicLinkJobAction &job,
                               const JobContext &context) const {
   llvm_unreachable("linking not implemented for this toolchain");
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const StaticLinkJobAction &job,
                               const JobContext &context) const {
   llvm_unreachable("archiving not implemented for this toolchain");
}

void ToolChain::addPathEnvironmentVariableIfNeeded(
   Job::EnvironmentVector &env, const char *name, const char *separator,
   options::FileTypeId optionID, const ArgList &args,
   ArrayRef<std::string> extraEntries) const {
   auto linkPathOptions = args.filtered(optionID);
   if (linkPathOptions.begin() == linkPathOptions.end() && extraEntries.empty())
      return;

   std::string newPaths;
   interleave(linkPathOptions,
              [&](const Arg *arg) { newPaths.append(arg->getValue()); },
              [&] { newPaths.append(separator); });
   for (auto extraEntry : extraEntries) {
      if (!newPaths.empty())
         newPaths.append(separator);
      newPaths.append(extraEntry.data(), extraEntry.size());
   }
   if (auto currentPaths = llvm::sys::Process::GetEnv(name)) {
      newPaths.append(separator);
      newPaths.append(currentPaths.getValue());
   }
   env.emplace_back(name, args.MakeArgString(newPaths));
}

void ToolChain::addLinkRuntimeLib(const ArgList &Args, ArgStringList &Arguments,
                                  StringRef LibName) const {
   SmallString<128> P;
   getClangLibraryPath(Args, P);
   llvm::sys::path::append(P, LibName);
   Arguments.push_back(Args.MakeArgString(P));
}

void ToolChain::getClangLibraryPath(const ArgList &Args,
                                    SmallString<128> &LibPath) const {
   const llvm::Triple &T = getTriple();

   getResourceDirPath(LibPath, Args, /*Shared=*/true);
   // Remove platform name.
   llvm::sys::path::remove_filename(LibPath);
   llvm::sys::path::append(LibPath, "clang", "lib",
                           T.isOSDarwin() ? "darwin"
                                          : get_platform_name_for_triple(T));
}

/// Get the runtime library link path, which is platform-specific and found
/// relative to the compiler.
void ToolChain::getResourceDirPath(SmallVectorImpl<char> &resourceDirPath,
                                   const llvm::opt::ArgList &args,
                                   bool shared) const {
   // FIXME: Duplicated from CompilerInvocation, but in theory the runtime
   // library link path and the standard library module import path don't
   // need to be the same.
   if (const Arg *A = args.getLastArg(options::OPT_resource_dir)) {
      StringRef value = A->getValue();
      resourceDirPath.append(value.begin(), value.end());
   } else if (!getTriple().isOSDarwin() && args.hasArg(options::OPT_sdk)) {
      StringRef value = args.getLastArg(options::OPT_sdk)->getValue();
      resourceDirPath.append(value.begin(), value.end());
      llvm::sys::path::append(resourceDirPath, "usr", "lib",
                              shared ? "polar" : "polar_static");
   } else {
      auto programPath = getDriver().getPHPProgramPath();
      resourceDirPath.append(programPath.begin(), programPath.end());
      llvm::sys::path::remove_filename(resourceDirPath); // remove /swift
      llvm::sys::path::remove_filename(resourceDirPath); // remove /bin
      llvm::sys::path::append(resourceDirPath, "lib",
                              shared ? "polar" : "polar_static");
   }
   llvm::sys::path::append(resourceDirPath,
                           get_platform_name_for_triple(getTriple()));
}

void ToolChain::getRuntimeLibraryPaths(SmallVectorImpl<std::string> &runtimeLibPaths,
                                       const llvm::opt::ArgList &args,
                                       StringRef SDKPath, bool shared) const {
   SmallString<128> scratchPath;
   getResourceDirPath(scratchPath, args, shared);
   runtimeLibPaths.push_back(scratchPath.str());

   if (!SDKPath.empty()) {
      scratchPath = SDKPath;
      llvm::sys::path::append(scratchPath, "usr", "lib", "swift");
      runtimeLibPaths.push_back(scratchPath.str());
   }
}

bool ToolChain::sanitizerRuntimeLibExists(const ArgList &args,
                                          StringRef sanitizerName,
                                          bool shared) const {
   SmallString<128> sanitizerLibPath;
   getClangLibraryPath(args, sanitizerLibPath);
   llvm::sys::path::append(sanitizerLibPath,
                           sanitizerRuntimeLibName(sanitizerName, shared));
   return llvm::sys::fs::exists(sanitizerLibPath.str());
}
